package io.plus.interactive;

import drds.common.$;
import drds.common.Uuid;
import io.plus.interactive.task.*;
import io.plus.interactive.user_interface.Packet;
import io.plus.interactive.user_interface.PacketTag;
import io.plus.utils.lock.ReadWriteLockSet;
import io.plus.utils.properties.ReadWriteLocPropertiesk;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.HashSet;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 最基本的标识id:uuid
 */
@Slf4j
@Data
public abstract class AsynchronousSocketChannelContext extends ReadWriteLocPropertiesk {
    public static final String UNKNOWN_IP = "UNKNOWN_IP";
    public static final AtomicInteger UNKNOWN_ADDRESS_PORT_SEQ = new AtomicInteger();
    private static final String DEFAULT_ATTUBITE_KEY = "io";
    public final ReentrantReadWriteLock closeLock = new ReentrantReadWriteLock();
    public final AsynchronousSocketChannelState asynchronousSocketChannelState = new AsynchronousSocketChannelState();
    public boolean isReconnect = false;

    /**
     * 此值不设时，心跳时间取org.tio.interactive.UserConfiguration.heartbeatTimeout
     * 当然这个值如果小于org.tio.interactive.UserConfiguration.heartbeatTimeout，定时检查的时间间隔还是以org.tio.interactive.UserConfiguration.heartbeatTimeout为准，只是在判断时用此值
     */
    public Long heartbeatTimeout = null;
    /**
     * 一个packet所需要的字节数（用于应用告诉框架，下一次解码所需要的字节长度，省去冗余解码带来的性能损耗）
     */
    public Integer packetNeededLength = null;
    public Configuration clientConfiguration = null;
    //
    public DecodeTask decodeTask = null;
    public HandlerTask handlerTask = null;
    public SendTask sendTask = null;
    //
    public WriteCompletionHandler writeCompletionHandler = null;
    public String userId;
    public boolean isWaitingClose = false;
    public boolean isClosed = true;
    public boolean isRemoved = false;
    public boolean isVirtual = false;
    public boolean hasTempDir = false;
    public AsynchronousSocketChannel asynchronousSocketChannel;
    public CloseTag closeTag = new CloseTag();
    private ReadCompletionHandler readCompletionHandler = null;
    private String token;
    private String businessId;
    private String id = null;
    private Node clientNode;
    private Node serverNode;
    /**
     * 该连接在哪些组中
     */
    private ReadWriteLockSet<String> groupIdSet = null;
    private Integer readBufferSize = null;                                            //个性化readBufferSize
    private CloseCode closeCode = CloseCode.INIT_STATUS;                        //连接关闭的原因码

    public AsynchronousSocketChannelContext(Configuration clientConfiguration, AsynchronousSocketChannel asynchronousSocketChannel) {
        super();
        init(clientConfiguration, asynchronousSocketChannel);

    }

    /**
     * 创建一个虚拟ChannelContext，主要用来模拟一些操作，譬如压力测试，真实场景中用得少
     */
    public AsynchronousSocketChannelContext(Configuration clientConfiguration) {
        this(clientConfiguration, Uuid.uuid());
    }

    /**
     * 创建一个虚拟ChannelContext，主要用来模拟一些操作，譬如压力测试，真实场景中用得少
     */
    public AsynchronousSocketChannelContext(Configuration clientConfiguration, String id) {
        isVirtual = true;
        this.clientConfiguration = clientConfiguration;
        Node clientNode = new Node("127.0.0.1", 26254);
        this.clientNode = clientNode;
        this.id = id;
        initOther();
    }

    private void assignAnUnknownClientNode() {
        Node clientNode = new Node(UNKNOWN_IP, UNKNOWN_ADDRESS_PORT_SEQ.incrementAndGet());
        setClientNode(clientNode);
    }

    /**
     * 创建Node
     */
    public abstract Node createClientNode(AsynchronousSocketChannel asynchronousSocketChannel) throws IOException;

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        AsynchronousSocketChannelContext other = (AsynchronousSocketChannelContext) obj;
        if (id == null) {
            return other.id == null;
        } else return id.equals(other.id);
    }

    /**
     * 等价于：getAttribute(DEFAULT_ATTUBITE_KEY)
     *
     * @return
     * @deprecated 建议使用get()
     */
    public Object getAttribute() {
        return get();
    }

    /**
     * 等价于：setAttribute(DEFAULT_ATTUBITE_KEY, value)<br>
     * 仅仅是为了内部方便，不建议大家使用<br>
     *
     * @deprecated 不建议各位同学使用这个方法，建议使用set("name1", object1)
     */
    public void setAttribute(Object value) {
        set(value);
    }

    /**
     * 等价于：getAttribute(DEFAULT_ATTUBITE_KEY)<br>
     * 等价于：getAttribute()<br>
     *
     * @return
     */
    public Object get() {
        return get(DEFAULT_ATTUBITE_KEY);
    }

    public Node getClientNode() {
        return clientNode;
    }


    public void setClientNode(Node clientNode) {
        if (!this.clientConfiguration.isShortConnection) {
            if (this.clientNode != null) {
                clientConfiguration.clientNodes.remove(this);
            }
        }

        this.clientNode = clientNode;
        if (this.clientConfiguration.isShortConnection) {
            return;
        }

        if (this.clientNode != null && !Objects.equals(UNKNOWN_IP, this.clientNode.getIp())) {
            clientConfiguration.clientNodes.put(this);
        }
    }

    @Override
    public int hashCode() {
        if ($.isNotNullAndNotEmpty(id)) {
            return this.id.hashCode();
        } else {
            return super.hashCode();
        }
    }

    public void init(Configuration configuration, AsynchronousSocketChannel asynchronousSocketChannel) {
        id = Uuid.uuid();
        this.setClientConfiguration(configuration);
        configuration.ids.bind(this);
        this.setAsynchronousSocketChannel(asynchronousSocketChannel);
        this.readCompletionHandler = new ReadCompletionHandler(this);
        this.writeCompletionHandler = new WriteCompletionHandler(this);

        initOther();
    }

    void initOther() {
        if (!clientConfiguration.isShortConnection) {
            //在长连接中，绑定群组几乎是必须要干的事，所以直接在初始化时给它赋值，省得在后面做同步处理
            groupIdSet = new ReadWriteLockSet<String>(new HashSet<>());
        }
    }

    public void processAfterSent(Packet packet, Boolean isSentSuccess) {
        isSentSuccess = isSentSuccess == null ? false : isSentSuccess;
        PacketTag packetTag = packet.getPacketTag();
        if (packetTag != null) {
            CountDownLatch countDownLatch = packetTag.getCountDownLatch();
            countDownLatch.countDown();
        }


    }

    /**
     * @param asynchronousSocketChannel the asynchronousSocketChannel to set
     */
    public void setAsynchronousSocketChannel(AsynchronousSocketChannel asynchronousSocketChannel) {
        this.asynchronousSocketChannel = asynchronousSocketChannel;
        if (asynchronousSocketChannel != null) {
            try {
                Node clientNode = createClientNode(asynchronousSocketChannel);
                setClientNode(clientNode);
            } catch (IOException e) {
                log.info(e.toString(), e);
                assignAnUnknownClientNode();
            }
        } else {
            assignAnUnknownClientNode();
        }
    }

    /**
     * 等价于：set(DEFAULT_ATTUBITE_KEY, value)<br>
     * 等价于：setAttribute(Object value)<br>
     *
     * @param value
     * @deprecated 不建议各位同学使用这个方法，建议使用set("name1", object1)
     */
    public void set(Object value) {
        set(DEFAULT_ATTUBITE_KEY, value);
    }

    /**
     * @param isClosed the isClosed to set
     */
    public void setClosed(boolean isClosed) {
        this.isClosed = isClosed;
        if (isClosed) {
            if (clientNode == null || !UNKNOWN_IP.equals(clientNode.getIp())) {
                String before = this.toString();
                assignAnUnknownClientNode();
                log.info("关闭前{}, 关闭后{}", before, this);
            }
        }
    }

    public void setClientConfiguration(Configuration configuration) {
        this.clientConfiguration = configuration;

        if (configuration != null) {
            decodeTask = new DecodeTask(this, configuration.lockCapableTaskThreadPoolExecutor);
            handlerTask = new HandlerTask(this, configuration.lockCapableTaskThreadPoolExecutor);
            sendTask = new SendTask(this, configuration.lockCapableTaskThreadPoolExecutor);
            configuration.connections.add(this);
        }
    }

    /**
     * 是否是服务器端
     */
    public abstract boolean isServer();

    public Integer getReadBufferSize() {
        if (readBufferSize != null && readBufferSize > 0) {
            return readBufferSize;
        }
        return this.clientConfiguration.getReadBufferSize();
    }


}
